//
//  L0PlugSuitSIMBLRegistry.m
//  PlugSuit
//
//  Created by ∞ on 10/3/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "L0PlugSuitSIMBLRegistry.h"
#import "L0PlugSuitFlavor.h"

@implementation L0PlugSuitSIMBLRegistry

- (id) init {
	if (self = [super init]) {
		_plugIns = [NSMutableDictionary new];
		[self updateRegistry];
	}
	
	return self;
}

- (void) dealloc {
	[_versionTooLow release];
	[_versionTooHigh release];
	[_incompatible release];
	[_plugIns release];
	[super dealloc];
}

- (BOOL) plugInsExistFor:(NSBundle*) app processID:(pid_t) pid {
	if (!L0PlugSuitIsCocoaApplication(pid))
		return NO;
	
	return [[self plugInsFor:app] count] > 0;
}

- (NSArray*) plugInsFor:(NSBundle*) app {
	if (!_versionTooLow)
		_versionTooLow = [NSMutableArray new];
	[_versionTooLow removeAllObjects];
	if (!_versionTooHigh)
		_versionTooHigh = [NSMutableArray new];
	[_versionTooHigh removeAllObjects];
	
	NSString* ident = [[app infoDictionary] objectForKey:@"CFBundleIdentifier"];
	double version = 0;
	NSNumber* versionNumber = [[app infoDictionary] objectForKey:@"CFBundleVersion"];
	if (versionNumber)
		version = [versionNumber doubleValue];
	
	NSMutableArray* result = [NSMutableArray array];
	
	NSEnumerator* enu = [[_plugIns allValues] objectEnumerator];
	NSBundle* plugIn;
	while (plugIn = [enu nextObject]) {
		if (![[self class] isEnabled:plugIn])
			continue;
		
		if (![[[plugIn infoDictionary] objectForKey:@"SIMBLTargetApplications"] isKindOfClass:[NSArray class]])
			continue;

		BOOL isAppropriate = NO;
		NSEnumerator* en2 = [[[plugIn infoDictionary] objectForKey:@"SIMBLTargetApplications"] objectEnumerator];
		NSDictionary* target;
		while (target = [en2 nextObject]) {
			if (![target isKindOfClass:[NSDictionary class]]) continue;
			
			if ([[target objectForKey:@"BundleIdentifier"] isEqual:@"*"]) {
				isAppropriate = YES; break;
			} else if ([ident isEqual:[target objectForKey:@"BundleIdentifier"]]) {
				// the above takes into account ident == nil
				id minVersion = [target objectForKey:@"MinVersion"];
				id maxVersion = [target objectForKey:@"MaxVersion"];
				
				if ((!minVersion || [minVersion doubleValue] <= version) &&
					(!maxVersion || [maxVersion doubleValue] >= version)) {
					isAppropriate = YES;
					break;
				} else if (minVersion && [minVersion doubleValue] > version)
					[_versionTooLow addObject:plugIn];
				else if (maxVersion && [maxVersion doubleValue] < version)
					[_versionTooHigh addObject:plugIn];
			}
		}
		
		if (isAppropriate)
			[result addObject:plugIn];
	}
	
	return result;
}

- (void) updateRegistry {
	[_plugIns removeAllObjects];
	
	NSEnumerator* libraries = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES) objectEnumerator];
	NSString* libraryPath;
	
	while (libraryPath = [libraries nextObject]) {
		libraryPath = [[[libraryPath 
						 stringByAppendingPathComponent:@"Application Support"]
						stringByAppendingPathComponent:@"SIMBL"]
					   stringByAppendingPathComponent:@"Plugins"];
		[self updateWithPlugInsInDirectory:libraryPath];
	}
}

- (void) updateWithPlugInsInDirectory:(NSString*) dir {
	NSEnumerator* enu = [[[NSFileManager defaultManager] directoryContentsAtPath:dir] objectEnumerator];
	NSString* item;
	
	while (item = [enu nextObject]) {
		item = [dir stringByAppendingPathComponent:item];
		NSBundle* b = [NSBundle bundleWithPath:item];
		NSString* ident;
		if (b && [b infoDictionary] && [[b infoDictionary] objectForKey:@"SIMBLTargetApplications"] && (ident = [[b infoDictionary] objectForKey:@"CFBundleIdentifier"])) {
			if (![_plugIns objectForKey:ident])
				[_plugIns setObject:b forKey:ident];
		}
	}
}

- (void) loadPlugIns {
	if ([[NSBundle bundleWithIdentifier:@"net.culater.SIMBL"] isLoaded]) {
		NSLog(@"[from PlugSuit] SIMBL is already loaded. PlugSuit will not load any SIMBL plug-ins for compatibility");
        NSLog(@"[from PlugSuit] SIMBL was detected as: %@", [NSBundle bundleWithIdentifier:@"net.culater.SIMBL"]);
		return;
	}
	
	if (!_incompatible)
		_incompatible = [NSMutableArray new];
	[_incompatible removeAllObjects];

	NSEnumerator* enu = [[self plugInsFor:[NSBundle mainBundle]] objectEnumerator];
	NSBundle* plugIn;
	
	while (plugIn = [enu nextObject]) {
#ifdef L0PlugSuitDebug
		NSLog(@"[from PlugSuit] DEBUG: Now loading SIMBL-style plug-in: %@", plugIn);
#endif
		if ([plugIn load])
			[plugIn principalClass]; // sends +load
		else
			[_incompatible addObject:plugIn];
	}
}

- (NSDictionary*) registeredPlugIns {
	return _plugIns;
}

- (NSArray*) versionTooHighOnLastLoad {
	return _versionTooHigh;
}

- (NSArray*) versionTooLowOnLastLoad {
	return _versionTooLow;
}

- (NSArray*) incompatibleOnLastLoad {
	return _incompatible;
}

@end
